#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using System;
using System.Threading;
using UnityEngine;
using UnityEngine.Networking;
using Cysharp.Threading.Tasks;

namespace Net.Unity
{
    public class DHParameter
    {
        public virtual DownloadHandler GetDownloadHandler()
        {
            return new DownloadHandlerBuffer();
        }
    }

    public class FileDHParameter : DHParameter
    {
        public string Path { get; set; }

        public FileDHParameter(string path)
        {
            Path = path;
        }

        public override DownloadHandler GetDownloadHandler()
        {
            return new DownloadHandlerFile(Path);
        }
    }

    public class TextureDHParameter : DHParameter
    {
        public bool Readable { get; set; } = true;

        public TextureDHParameter(bool readable)
        {
            Readable = readable;
        }

        public override DownloadHandler GetDownloadHandler()
        {
            return new DownloadHandlerTexture(Readable);
        }
    }

    public static class UnityWebRequestEx
    {
        public static async UniTask<T> GetAsync<T>(string url, Func<UnityWebRequest, T> onComplete, Func<string, UniTask<ValueTuple<bool, T>>> onError, float timeout)
        {
            return await GetAsync(url, new DHParameter(), null, onComplete, onError, timeout);
        }

        public static async UniTask<T> GetAsync<T>(string url, Func<UnityWebRequest, T> onComplete, Func<string, UniTask<bool>> onError, float timeout)
        {
            return await GetAsync(url, new DHParameter(), null, onComplete, async onError1 => (await onError(onError1), default), timeout);
        }

        public static async UniTask<T> GetAsync<T>(string url, DHParameter parameter, Action<UnityWebRequestAsyncOperation> onProgress, Func<UnityWebRequest, T> onComplete, Func<string, UniTask<ValueTuple<bool, T>>> onError, float timeout)
        {
            return await GetAsync(url, "GET", parameter, onProgress, onComplete, onError, timeout);
        }

        public static async UniTask<T> GetAsync<T>(string url, string method, DHParameter parameter, Action<UnityWebRequestAsyncOperation> onProgress, Func<UnityWebRequest, T> onComplete, Func<string, UniTask<ValueTuple<bool, T>>> onError, float timeout)
        {
            UnityWebRequest request = null;
            try
            {
                if (!url.StartsWith("http") && !url.StartsWith("jar:file")) //安卓平台流路径时会自动包含jar:file，如果是热更新模式则需要补充file://
                {
#if UNITY_ANDROID && !UNITY_EDITOR
                    url = "file://" + url;
#endif
                }
            J:
                var downloadHandler = parameter.GetDownloadHandler();
                request = new UnityWebRequest(url, method, downloadHandler, null)
                {
                    timeout = -1
                };
                var oper = request.SendWebRequest();
                var dateTime = DateTime.Now.AddSeconds(timeout);
                var downloadedBytes = 0UL;
                while (!oper.isDone && string.IsNullOrEmpty(request.error) && DateTime.Now < dateTime)
                {
                    onProgress?.Invoke(oper);
                    if (request.downloadedBytes != downloadedBytes)
                    {
                        downloadedBytes = request.downloadedBytes;
                        dateTime = DateTime.Now.AddSeconds(timeout);
                    }
                    await UniTask.Yield();
                }
                if (!string.IsNullOrEmpty(request.error) | DateTime.Now > dateTime)
                {
                    var (retry, fail) = await onError(request.error);
                    if (retry)
                    {
                        request.Dispose();
                        goto J;
                    }
                    return fail;
                }
                return onComplete(request);
            }
            catch (Exception ex)
            {
                Debug.LogError($"{url} -- {ex}");
            }
            finally
            {
                request?.Dispose();
            }
            return default;
        }

        public static T Get<T>(string url, Func<UnityWebRequest, T> onComplete, Action<string> onError, float timeout)
        {
            return Get(url, new DHParameter(), onComplete, onError, timeout);
        }

        public static T Get<T>(string url, DHParameter parameter, Func<UnityWebRequest, T> onComplete, Action<string> onError, float timeout)
        {
            UnityWebRequest request = null;
            try
            {
                if (!url.StartsWith("http") && !url.StartsWith("jar:file")) //安卓平台流路径时会自动包含jar:file，如果是热更新模式则需要补充file://
                {
#if UNITY_ANDROID && !UNITY_EDITOR
                    url = "file://" + url;
#endif
                }
                request = new UnityWebRequest(url, "GET", parameter.GetDownloadHandler(), null)
                {
                    timeout = -1
                };
                var oper = request.SendWebRequest();
                var dateTime = DateTime.Now.AddSeconds(timeout);
                var downloadedBytes = 0UL;
                while (!oper.isDone && string.IsNullOrEmpty(request.error) && DateTime.Now < dateTime)
                {
                    if (request.downloadedBytes != downloadedBytes)
                    {
                        downloadedBytes = request.downloadedBytes;
                        dateTime = DateTime.Now.AddSeconds(timeout);
                    }
                    Thread.Yield();
                }
                if (!string.IsNullOrEmpty(request.error) | DateTime.Now > dateTime)
                {
                    onError?.Invoke(request.error);
                    return default;
                }
                return onComplete(request);
            }
            catch (Exception ex)
            {
                Debug.LogError(ex);
            }
            finally
            {
                request?.Dispose();
            }
            return default;
        }

        public static void GetVoid(string url, Action<UnityWebRequest> onComplete, Action<string> onError, float timeout)
        {
            GetVoid(url, new DHParameter(), onComplete, onError, timeout);
        }

        public static void GetVoid(string url, DHParameter parameter, Action<UnityWebRequest> onComplete, Action<string> onError, float timeout)
        {
            UnityWebRequest request = null;
            try
            {
                if (!url.StartsWith("http") && !url.StartsWith("jar:file")) //安卓平台流路径时会自动包含jar:file，如果是热更新模式则需要补充file://
                {
#if UNITY_ANDROID && !UNITY_EDITOR
                    url = "file://" + url;
#endif
                }
                request = new UnityWebRequest(url, "GET", parameter.GetDownloadHandler(), null)
                {
                    timeout = -1
                };
                var oper = request.SendWebRequest();
                var dateTime = DateTime.Now.AddSeconds(15f);
                var downloadedBytes = 0UL;
                while (!oper.isDone && string.IsNullOrEmpty(request.error) && DateTime.Now < dateTime)
                {
                    if (request.downloadedBytes != downloadedBytes)
                    {
                        downloadedBytes = request.downloadedBytes;
                        dateTime = DateTime.Now.AddSeconds(15f);
                    }
                    Thread.Yield();
                }
                if (!string.IsNullOrEmpty(request.error) | DateTime.Now > dateTime)
                {
                    onError?.Invoke(request.error);
                    return;
                }
                onComplete(request);
            }
            catch (Exception ex)
            {
                Debug.LogError(ex);
            }
            finally
            {
                request?.Dispose();
            }
        }
    }
}
#endif